// Copyright (c) 2010 School of Software, Tsinghua University, Beijing (China)
// All rights reserved.
//
// This file is part of Geoxiom (http://code.google.com/p/geoxiom/); you may 
// redistribute it under the terms of the GNU Lesser General Public License.
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// Author(s) : Kan-Le Shi

#ifndef GEOXIOM_LEX_ELEMENT_HPP
#define GEOXIOM_LEX_ELEMENT_HPP

#include <string>

namespace Geoxiom {
namespace Lex {

/**
 * @brief This class maintains the element structure of the results of lexical
 *        analysis. As declared in the subenum Type, there are several sup-
 *        ported types. The buffer holds the data of the values. For an in-
 *        teger, double, or a string with length no more than buffer_max,
 *        data are directly stores in the buffer space. Or, the first four, 
 *        or eight (depending on 32/64-bit compiler) bytes are used to store
 *        a pointer to an assigned structure in heap. The final byte is such
 *        a flag showing whether it is assigned outside.
 *        
 *        The field source stands for the original code from the source text.
 *        for example, a decimal 314, may have a source `3.14e2' as code.
 *        The fields line and column specify the starting position of it.
 */
typedef class Element {
public:
	enum Type {
		unknown,
		identifier,
		symbol,
		integer,
		decimal,
		string,
		wildcard	// this symbol is only used in grammar analysis
	};
	
	enum Symbol	{
		period,
		comma,
		semicolon,
	};

protected:
	static const int buffer_max = 11;
	Type type_;
	char buffer_[buffer_max + 1];

public:
	std::string source;
	int line;
	int column;

protected:
	int get_int_() const { return *((int *)(buffer_)); }
	void set_int_(int value) const { *((int *)(buffer_)) = value; }
	double get_double_() const { return *((double *)(buffer_)); }
	void set_double_(double value) const { *((double *)(buffer_)) = value; }
	std::string get_string_() const {
		if (buffer_[buffer_max] == 0)
			return std::string(buffer_);
		else
			return **((std::string **)(buffer_));
	}
	void set_string_(const std::string &value) {
		if (value.length() <= buffer_max) {
			size_t length = value.length();
			const char *p = value.data();
			for (size_t i = 0; i < length; i++)
				buffer_[i] = *(p++);
			buffer_[length] = 0;
			buffer_[buffer_max] = 0;
		} else {
			*((std::string **)buffer_) = new std::string(value);
			buffer_[buffer_max] = 1;
		}
	}

	void reset(bool release, bool clear_source_info) {
		if (release)
			if (buffer_[buffer_max] != 0)
				delete (*((std::string **)(buffer_)));
		type_ = unknown;
		for (int i = 0; i <= buffer_max; i++)
			buffer_[i] = 0;
		if (clear_source_info) {
			source = std::string();
			line = 0;
			column = 0;
		}
	}

public:
	std::string get_identifier() const { return get_string_(); }
	Symbol get_symbol() const { return (Symbol)get_int_(); }
	int get_integer() const { return get_int_(); }
	double get_decimal() const { return get_double_(); }
	std::string get_string() const { return get_string_(); }
	int get_wildcard() const { return get_int_(); }
	Type get_type() const { return type_; }

	bool set(Type type, const std::string &value) {
		if ((type != identifier) && (type != string)) 
			return false;
		reset(true, false);
		type_ = type;
		set_string_(value);
		return true;
	}
	bool set(Type type, Symbol value) {
		if (type != symbol)
			return false;
		reset(true, false);
		type_ = type;
		set_int_((int)value);
		return true;
	}
	bool set(Type type, int value) {
		if ((type != integer) && (type != wildcard))
			return false;
		reset(true, false);
		type_ = type;
		set_int_(value);
		return true;
	}
	bool set(Type type, double value) {
		if (type != decimal)
			return false;
		reset(true, false);
		type_ = type;
		set_double_(value);
		return true;
	}

	void set(const std::string &new_source, int new_line, int new_column) {
		source = new_source;
		line = new_line;
		column = new_column;
	}

public:
	void operator=(const Element &e) {
		reset(true, false);
		type_ = e.type_;
		if (e.buffer_[buffer_max] == 0) {
			for (int i = 0; i <= buffer_max; i++)
				buffer_[i] = e.buffer_[i];
		} else {
			*((std::string **)buffer_) = new std::string(**((std::string **)e.buffer_));
			buffer_[buffer_max] = 1;
		}
		source = e.source;
		line = e.line;
		column = e.column;
	}

public:
	Element() { reset(false, true); }
	Element(const Element& e) { reset(false, true); *this = e; }
	~Element() { reset(true, false); }
} *ElementPtr, *ElementArray;

}
}

#endif
